Flutter 页面构建渲染更新流程解析 您所在的位置:网站首页 flutter 分帧上屏 Flutter 页面构建渲染更新流程解析

Flutter 页面构建渲染更新流程解析

2023-12-18 17:50| 来源: 网络整理| 查看: 265

1、UI 编程的两种方式

目前主流的UI编写方式主要有两种,命令式UI和声明式UI。在iOS开发中使用的UI编程方式是命令式UI(Imperative UI),而声明式UI的意思就是让开发者描述需要一个什么样的界面。在Flutter中就是采用了声明式UI(Declarative UI)

下面这样一个简单的例子:

declarativeUIchanges.png

在iOS中代码需要这么写

viewB.backgroundColor = [UIColor red]; for (UIView *view in viewB.subviews) { [view removeFromSuperview]; } UIView *viewC3 = [[UIView alloc] initWithFrame:CGRectMake(100,80,120,40)]; [viewB addSubview:viewC3];

在Flutter声明式UI中只需要这样

return ViewB( color: red, child: ViewC(...), )

声明式 UI 相对来说减轻了开发者的负担,不需要考虑如何调用 UI 实例的方法来改变不同的状态,只需要开发者描述当前的 UI 状态 (即各属性的值),框架会自动将 UI 从之前的状态切换到开发者描述的当前状态。

2、Flutter渲染三棵树

在官方文档中对Widget描述如下:

/// Describes the configuration for an [Element]. /// /// Widgets are the central class hierarchy in the Flutter framework. A widget /// is an immutable description of part of a user interface. Widgets can be /// inflated into elements, which manage the underlying render tree.

主要包含以下几个信息

Widget是指一部分 UI 的描述 Widget是不可变的 Widget是对Element配置的描述,而Element管理着底层的渲染树

第一个信息显而易见,各个Widget会根据我们所写的属性 (在 Flutter 里指状态 State) 展示在屏幕上。那么第二个信息,如果Widget是不可变的,而且用的是声明式 UI,那么随着用户的操作或者数据的变更,UI 是怎么更新的

Flutter 渲染树 简介

trees.png 在Flutter中,除了Widget树,还有Element树和Render树,这三棵树各司其职,完成了Flutter的渲染更新。

主要功能:

Widget是Element的配置描述,持有公共属性和提供公开方法。Widget仅仅只持有控件的配置信息,并不会参与UI的渲染。所以即是widget会频繁的创建和销毁,也不会影响到渲染的性能。从渲染的角度进行分类,分为可渲染Widget与不可渲染Widget,只有生成Element对应为RenderObjectElement和它的子类才有RenderObject可以渲染到页面上,我们常用的 StatelessWidget 与 StatefulWidget 就属于不可渲染的Widge Element是Widget在树中特定位置的一个实例,这个是真正的节点,用来关联Widget与渲染对象。每一个Widget都对应着一个Element,Widget实例会经常变动,但是渲染树不能经常改变,因为实例化一个RenderObject的成本是很高的,频繁的实例化和销毁RenderObject对性能的影响比较大,所以当Widget树改变的时候,Flutter使用Element树来比较新的Widget树和原来的Widget树,如果某一个位置的Widget和新Widget一致时,则只需要修改RenderObject的配置,不用进行耗费性能的RenderObject的实例化工作了 (具体看 updateChild 函数解析) Render树中的对象,主管渲染,测量自身Size并进行绘制放置子节点

这三者的关系:

根据Widget生成Element,然后创建相应的RenderObject并关联到Element.renderObject属性上,最后再通过RenderObject来完成布局排列和绘制。每一个 Widget 都会有其对应的 Element,但是只有需要渲染的Widget才会有对应的RenderObject

QQ20211128-212802@2x.png

具体对应关系如下:

WidgetElementRenderObjectStatelessWidgetStatelessElement-StatefulWidgetStatefulElement-ProxyWidgetProxyElement-InheritedWidgetInheritedElement-SingleChildRenderObjectWidgetSingleChildRenderObjectElementRenderObjectMultiChildRenderObjectWidgetMultiChildRenderObjectElementRenderObjectRenderObjectWidgetRenderObjectElementRenderObject 3、Flutter 页面渲染 Flutter渲染流程

对iOS的原生绘制来说,UIView 树由上到下遍历每一个 UIView,UIView进行Constraint—Layout—Display。而在 Flutter开 发中界面是由 Widget 组成的,渲染在 Framework 层会有 Build、Layout、Paint、Composite Layer 等几个阶段。 将 Layer 进行组合,生成纹理,使用 OpenGL 的接口向 GPU 提交渲染内容进行光栅化与合成,是在 Flutter 的 C++ 层,使用的是 Skia 库代替原生的 Core Graphics。

Build: 开始构建 Widget,将 UI 配置转换成可渲染的数据结构 Layout: 确定每个 Widget 的位置和大小 Paint: 将 Widget 绘制成用户看到的样子,生成图层或者 Texture Composite: 把Paint过程生成图层或纹理按照顺序进行组合合成,以便可以高效的将 Widget 呈现到屏幕上 Flutter渲染时机

在Flutter应用中,触发渲染(树的更新)主要有以下几种时机:

在Futter启动时runApp(Widget app)全局刷新 开发者主动调用setState()方法: 将该子树做StatefullWidget的一个子widget,并创建对应的State类实例,通过调用state.setState() 触发该子树的刷新 热重载

7672680-244762ddf72290d4.jpg 通过上面三种方式,发通知 Flutter的framework通知状态发生改变,Framework通知Engine渲染,Engine等下一个Vsync(垂直同步信号)到来后,触发Framework开始执行渲染操作(UI线程),生成LayerTree传递给Engine,Engine的GPU线程进行合成和光栅化等操作后展示到屏幕上

build 在配置好Widget后,Flutter会生成一个对应的Element,而Element又会调用Widget的方法生成一个RenderObject, 递归调用遍历子节点这样就生成了三棵树,Element同时持有Widget和RenderObject的引用 而需要更新的时候会从标记了_dirty的子树开始,对比每个子节点Widget的runtimeType和key,这时会有两种情况:1、Widget 的 runtimeType 及 key 的值相同,认为是同一个Widget,Flutter会复用其对应的Element和RenderObject节点,只更新RenderObject的属性值,最终从Render树中找到渲染对象并将其更新。2、runtimeTpe或key的值不同,则认为不是同一个Widget,不可复用,需要重新创建对应的Element和RenderObject Layout

image.png

Layout的整体过程是在RenderObject中进行的,Constraints主要提供了minWidth、maxWidth、minHeight、maxHeight4个属性,用以对子节点进行约束,接收该Constraints的子节点在计算自己的大小时就有了两个条件:

minWidth RootRenderObjectElement->RenderObjectElement->Element

RenderObjectElement主要执行的代码是使用 Widget 的 createRenderObject方法生成 RenderObject并且赋值给Element

abstract class RenderObjectElement extends Element { @override RenderObjectWidget get widget => super.widget as RenderObjectWidget; @override RenderObject get renderObject => _renderObject!; RenderObject? _renderObject; @override void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); ...... // 通过widget树调用createRenderObject方法传入Element实例自己获取RenderObject渲染树。 _renderObject = widget.createRenderObject(this); attachRenderObject(newSlot); _dirty = false; ...... } @override void attachRenderObject(Object? newSlot) { assert(_ancestorRenderObjectElement == null); _slot = newSlot; // 寻找可用的父RenderObject,再添加新的节点 _ancestorRenderObjectElement = _findAncestorRenderObjectElement(); _ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot); final ParentDataElement? parentDataElement = _findAncestorParentDataElement(); if (parentDataElement != null) _updateParentData(parentDataElement.widget); } }

基类Element的mount方法,仅仅是把parent记录在此element中,更新slot和depth信息

abstract class Element extends DiagnosticableTree implements BuildContext { void mount(Element? parent, Object? newSlot) { ...... // 查找父 element ,获取父element对自己的布局约束等 _parent = parent; _slot = newSlot; _lifecycleState = _ElementLifecycle.active; _depth = _parent != null ? _parent!.depth + 1 : 1; if (parent != null) { // BuildOwner 类型,从 parent 传过来,跟节点的在初始化的时候设置, // 整个 App 只存在一个 BuildOwner _owner = parent.owner; } final Key? key = widget.key; if (key is GlobalKey) { owner!._registerGlobalKey(key, this); } _updateInheritance(); } }

再看 _rebuild 方法

class RenderObjectToWidgetElement extends RootRenderObjectElement { ...... void _rebuild() { try { // updateChild同样也是界面创建与刷新时的重要处理过程,后面会详细说明, // 这里只需要认为这里会进行子控件的添加,而且是递归添加处理,分别调用子控件的mount操作。 // 其中widget.child就是我们传入的Widget实例 _child = updateChild(_child, widget.child, _rootChildSlot); } catch (exception, stack) { final FlutterErrorDetails details = FlutterErrorDetails( exception: exception, stack: stack, library: 'widgets library', context: ErrorDescription('attaching to the render tree'), ); FlutterError.reportError(details); final Widget error = ErrorWidget.builder(details); _child = updateChild(null, error, _rootChildSlot); } } ... }

到这里可以简单的理解为RenderObjectToWidgetElement初始化时绑定了它与RenderObjectToWidgetAdapter既 Element与Widget,然后调用了mount方法生成RenderObject绑定了它与RenderObject的关系既 Element与Render的关系。然后递归调用了updateChild()方法,生成了整个渲染树

目前加载过程流程图如下: image.png

updateChild(创建页面时) abstract class Element extends DiagnosticableTree implements BuildContext { Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { ...... final Element newChild; // 第一次,Element child是null,执行else里的逻辑, // inflateWidget使用子widget来创建一个子Element if (child != null) { ...... } else { newChild = inflateWidget(newWidget, newSlot); } ...... return newChild; } Element inflateWidget(Widget newWidget, Object? newSlot) { assert(newWidget != null); final Key? key = newWidget.key; // 如果 widget的Key是GlobalKey的话,会先从GlobalKey中获取引用的Element, // 如果有lement的话就更新复用 if (key is GlobalKey) { final Element? newChild = _retakeInactiveElement(key, newWidget); if (newChild != null) { assert(newChild._parent == null); assert(() { _debugCheckForCycles(newChild); return true; }()); newChild._activateWithParent(this, newSlot); final Element? updatedChild = updateChild(newChild, newWidget, newSlot); assert(newChild == updatedChild); return updatedChild!; } } // 否则就用 widget调用其createElement()来创建了一个element // Element初始化需要Widget参数,创建完成后newChild的widget参数就是newWidget final Element newChild = newWidget.createElement(); assert(() { _debugCheckForCycles(newChild); return true; }()); // 接着就调用新建的子element的mount方法 newChild.mount(this, newSlot); assert(newChild._lifecycleState == _ElementLifecycle.active); return newChild; } }

mount方法比较复杂根据不同的 element 类型有几种分支, element是个抽象类有两个抽象子类, RenderObjectElement 和 ComponentElement,他们各自还有自己的字类,具体类如下图

image.png

具体代码调用过程如下:ComponentElement类的mount方法

abstract class ComponentElement extends Element { void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); assert(_child == null); assert(_lifecycleState == _ElementLifecycle.active); _firstBuild(); assert(_child != null); } void _firstBuild() { rebuild(); } @override void performRebuild() { ...... Widget? built; try { ...... // build函数为子类StatefulElement和StatelessElement的build方法 built = build(); ..... } catch (e, stack) { ..... } finally { ...... } ...... try { _child = updateChild(_child, built, slot); } catch (e, stack) { ...... } ...... } } class StatelessElement extends ComponentElement { ...... @override Widget build() => widget.build(this); ...... } class StatefulElement extends ComponentElement { ...... @override Widget build() => state.build(this); ...... }

在 Flutter 里面最常见的 StatelessWidget 和 StatefulWidget 的 build 方法就是在这里被调用的。然后就又调用到了updateChild方法,这就回到了上边流程一直往下遍历创建widget树。

SingleChildRenderObjectElement类和MultiChildRenderObjectElement类的mount方法

class SingleChildRenderObjectElement extends RenderObjectElement { @override void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); _child = updateChild(_child, widget.child, null); } } class MultiChildRenderObjectElement extends RenderObjectElement { @override void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); final List children = List.filled(widget.children.length, _NullElement.instance, growable: false); Element? previousChild; for (int i = 0; i < children.length; i += 1) { final Element newChild = inflateWidget(widget.children[i], IndexedSlot(i, previousChild)); children[i] = newChild; previousChild = newChild; } _children = children; } }

SingleChildRenderObjectElement类和MultiChildRenderObjectElement类都继承自RenderObjectElement,这个方法之前已经看过主要执行的代码是使用 Widget 的 createRenderObject方法生成 RenderObject并且赋值给Element,然后寻找到寻找可用的父RenderObject,再添加新的节点

通过代码可以看到 Element 调用mount()方法时

componentElement的mount方法主要作用是执行build(根据类型区分widget.build, state.build) renderObjectElement 的mount方法主要作用是生成RenderObject Element创建完成时就会调用 mount, 调用顺序为 mount -> _firstBuild -> reBuild -> performRebuild -> build updateChild总结

总结一下:updateChild是一个递归的过程,总结下来有下面几个步骤

Element如果是RenderObjectElement则创建RenderObject,并从祖先找到上一个RenderObjectElement,然后调用祖先RenderObjectElement的RenderObject的insertRenderObjectChild方法插入创建的RenderObject 如果子widget需要build出来就调用build方法创建子widget,如果不需要直接在成员变量可以拿到子widget 调用子widget的createElement创建子Element 调用子Element的mount方法将子Element的parent设置成自己,然后子Element去到第1步

构建流程如下:

image.png

scheduleAttachRootWidget 函数完成Flutter App 中的 Widget、Element 和 RenderObject树生成和相互关联。在函数最后调用了SchedulerBinding.instance!.ensureVisualUpdate(); 通知Engine有UI需要更新渲染页面 (后面详细描述)

总结一下runApp方法的大体过程

调用runApp(Widget)函数传入一个Widget作为根Widget。 Widget只是一个配置类,不是实际的UI元素。 runApp通过WidgetsFlutterBindingmixIn继承一众父类进行初始化。 其中,RendererBinding 中的 renderView对象,是实际的渲染对象。 通过RenderObjectToWidgetAdapter类(继承自 RenderObjectWidget我们 runApp 中传递的 Widget 树就被追加到了这个树根的 child 属性上)生成一个RenderObjectToWidgetElement类型的Element作为根Element,并让Widget、renderView和BuildOwner和根Element产生关系,然后通过 mount 方法生成树形结构。 最后调用SchedulerBinding.instance.ensureVisualUpdate()函数,等待下一帧渲染 scheduleAttachRootWidget是一个耗时操作,异步运行。runApp会优先调用scheduleWarmUpFrame()渲染预热帧。

页面构建流程如下:

image.png

更新页面 setState

在 Flutter 中我们直接通过 setState 方法来对页面进行刷新,所以直接查看源码,去掉了 assert 异常处理相关代码

abstract class State with Diagnosticable { @protected void setState(VoidCallback fn) { final Object? result = fn() as dynamic; _element!.markNeedsBuild(); } }

直接调用 setState 传入的函数,然后调用 Element 的 markNeedsBuild 方法

abstract class Element extends DiagnosticableTree implements BuildContext { void markNeedsBuild() { if (_lifecycleState != _ElementLifecycle.active) return; ..... if (dirty) return; _dirty = true; owner!.scheduleBuildFor(this); } }

这里面将 Element 标记为 dirty,然后调用 BuildOwner 类的 scheduleBuildFor 方法,BuildOwner 实例在 WidgetsBinding 中初始化整个App中只有一个实例

BuildOwner.scheduleBuildFor

继续查看:

class BuildOwner { void scheduleBuildFor(Element element) { ...... // 判断 element 是否已经加入到 _dirtyElements 列表中, // 若是已经在列表中,就直接返回,不用再执行下面的操做 if (element._inDirtyList) { _dirtyElementsNeedsResorting = true; return; } // 判断 _scheduledFlushDirtyElements 是否为 false ,这个变量表示当前是否正在 rebuild // _dirtyElements 中的元素。若是没有正在 rebuild ,而且 onBuildScheduled 回调不为空 // 就调用 onBuildScheduled 函数 if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { _scheduledFlushDirtyElements = true; onBuildScheduled!(); } _dirtyElements.add(element); element._inDirtyList = true; ...... } }

这里将该element加入到_dirtyElements中,标记这个节点刷新时需要进行处理。onBuildScheduled 方法在初始化中设置的具体代码为 WidgetsBinding 中 buildOwner!.onBuildScheduled = _handleBuildScheduled;具体代码如下

mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { void _handleBuildScheduled() { ensureVisualUpdate(); } }

直接调用到SchedulerBinding类的ensureVisualUpdate方法

mixin SchedulerBinding on BindingBase { void ensureVisualUpdate() { switch (schedulerPhase) { case SchedulerPhase.idle: case SchedulerPhase.postFrameCallbacks: scheduleFrame(); return; case SchedulerPhase.transientCallbacks: case SchedulerPhase.midFrameMicrotasks: case SchedulerPhase.persistentCallbacks: return; } } void scheduleFrame() { if (_hasScheduledFrame || !framesEnabled) return; ensureFrameCallbacksRegistered(); // 执行代码 // void scheduleFrame() => platformDispatcher.scheduleFrame(); // void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'; // 调用 Flutter Engine 方法 window.scheduleFrame(); _hasScheduledFrame = true; } @protected void ensureFrameCallbacksRegistered() { // 调用 void scheduleFrame() native 'PlatformConfiguration_scheduleFrame' // 在下一个适当的机会调用 onBeginFrame 和 onDrawFrame 回调 () // onBeginFrame 主要进行是做一些准备工作,让framework准备好绘制工作,例如重新设置状态、变量等等 window.onBeginFrame ??= _handleBeginFrame; window.onDrawFrame ??= _handleDrawFrame; } void _handleDrawFrame() { ...... handleDrawFrame(); } void handleDrawFrame() { assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks); Timeline.finishSync(); // end the "Animate" phase try { // 执行 _persistentCallbacks 数组内的 callback // _persistentCallbacks 数组在初始化 WidgetsBinding 中 addPersistentFrameCallback 方法插入 _schedulerPhase = SchedulerPhase.persistentCallbacks; for (final FrameCallback callback in _persistentCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp!); ...... } finally { ...... _currentFrameTimeStamp = null; } } } SchedulerBinding.instance.ensureVisualUpdate

经过一系列的函数调用,调用 SchedulerBinding.instance.ensureVisualUpdate 最后会调用到WidgetsBinding中 addPersistentFrameCallback 设置的方法

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { void _handlePersistentFrameCallback(Duration timeStamp) { drawFrame(); _scheduleMouseTrackerUpdate(); } } WidgetsBinding.drawFrame

drawFrame函数有两个,一个在 WidgetsBinding 内一个在 RendererBinding内,优先调用WidgetsBinding内的函数

@override mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { void drawFrame() { ...... try { if (renderViewElement != null) buildOwner!.buildScope(renderViewElement!); super.drawFrame(); // 清理不再使用的 element buildOwner!.finalizeTree(); } finally { ...... } ...... } }

又调用到了buildOwner.buildScope方法,之前创建界面时调用了这个方法,现在刷新时也用到了,创建页面时只是简单的看成只执行了 block 方法,现在详细说明一下:

void buildScope(Element context, [ VoidCallback? callback ]) { if (callback == null && _dirtyElements.isEmpty) return; ...... Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent); try { _scheduledFlushDirtyElements = true; if (callback != null) { ...... _dirtyElementsNeedsResorting = false; try { callback(); } finally { ...... } } // 首先将_dirtyElements进行排序,这是因为节点可能有很多个, // 如果其中两个节点存在级联关系,父级的Widget build操作必然会调用到子级的Widget build, // 如果子级又自己build一次,相当于出现了重复操作。因此通过深度排序就会避免这个问题 _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; int dirtyCount = _dirtyElements.length; int index = 0; // 对每一个Element进行遍历 while (index < dirtyCount) { ...... try { // 执行rebuild操作 _dirtyElements[index].rebuild(); } catch (e, stack) { ...... } index += 1; // 如果在遍历过程中增加了新的节点,那么就需要重新排序 if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) { _dirtyElements.sort(Element._sort); _dirtyElementsNeedsResorting = false; dirtyCount = _dirtyElements.length; while (index > 0 && _dirtyElements[index - 1].dirty) { index -= 1; } } } ...... return true; }()); } finally { // 所有Element都rebuild后,清空 _dirtyElements 集合,节点状态恢复正常 for (final Element element in _dirtyElements) { assert(element._inDirtyList); element._inDirtyList = false; } _dirtyElements.clear(); _scheduledFlushDirtyElements = false; _dirtyElementsNeedsResorting = null; Timeline.finishSync(); ...... } assert(_debugStateLockLevel >= 0); } Element.rebuild

Element.rebuild()方法调用了子类的 performRebuild()方法ComponentElement类页面创建过程看到过,在更新时会再次调用

@override void performRebuild() { ...... Widget? built; try { ...... // build函数为子类StatefulElement和StatelessElement的build方法 built = build(); ..... } catch (e, stack) { ..... } finally { ...... _dirty = false; } ...... try { _child = updateChild(_child, built, slot); } catch (e, stack) { ...... } ...... }

最后还是回到 updateChild 方法,构建时只看了 child 为空的情况,现在看 child 不为空只更新的情况

abstract class Element extends DiagnosticableTree implements BuildContext { Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { // 如果不存在新的Widget,那么说明这一个节点应该取消掉了, // 执行deactivateChild 删除节点方法。 if (newWidget == null) { if (child != null) deactivateChild(child); return null; } final Element newChild; if (child != null) { bool hasSameSuperclass = true; if (hasSameSuperclass && child.widget == newWidget) { // 如果子节点的widget和新的widget一致(这里的一致指的是同一个对象) // 直接返回这个子节点。 if (child.slot != newSlot) updateSlotForChild(child, newSlot); newChild = child; } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { // 如果两个widget不是同一个对象,判断类型是否相同,通过canUpdate方法判断 // 依据是Widget类型一致,同时Key一致 // 这种情况下,只需要更新子节点 // 因此这一步就是widget变更,但是element不变更 if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); assert(child.widget == newWidget); assert(() { child.owner!._debugElementWasRebuilt(child); return true; }()); newChild = child; } else { // 其它情况下则认为子节点是新增的,先删除原来的再建新的 // 调用`inflateWidget`进行子节点创建 // 里面与创建界面相同,执行了mount操作 deactivateChild(child); assert(child._parent == null); newChild = inflateWidget(newWidget, newSlot); } } else { ...... } ...... return newChild; } }

element.update方法会把newWidget记录下来

abstract class Element extends DiagnosticableTree implements BuildContext { @mustCallSuper void update(covariant Widget newWidget) { _widget = newWidget; } }

StatelessElement.update方法会调用rebuild,rebuild中会调用performRebuild()去重建其子widget,类似一个递归的流程

class StatelessElement extends ComponentElement { @override void update(StatelessWidget newWidget) { super.update(newWidget); _dirty = true; rebuild(); } }

StatefulElement.update方法,先回调state.didUpdateWidget(这里就是我们在自定义Widget写的生命周期回调函数就是在这里触发的),最后又调用rebuild

@override class StatefulElement extends ComponentElement { void update(StatefulWidget newWidget) { super.update(newWidget); final StatefulWidget oldWidget = state._widget!; _dirty = true; state._widget = widget as StatefulWidget; try { final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic; } finally { } rebuild(); } }

SingleChildRenderObjectElement.update方法,调用 updateChild

class SingleChildRenderObjectElement extends RenderObjectElement { @override void update(SingleChildRenderObjectWidget newWidget) { super.update(newWidget); assert(widget == newWidget); _child = updateChild(_child, widget.child, null); } }

MultiChildRenderObjectElement.update方法,调用 updateChildren,内部循环调用updateChild方法

class MultiChildRenderObjectElement extends RenderObjectElement { @override void update(MultiChildRenderObjectWidget newWidget) { super.update(newWidget); assert(widget == newWidget); _children = updateChildren(_children, widget.children, forgottenChildren: _forgottenChildren); _forgottenChildren.clear(); } }

RenderObjectElement.update方法,update方法里面只是更新widget的配置,这里会对 renderObject进行修改

abstract class RenderObjectElement extends Element { @override void update(covariant RenderObjectWidget newWidget) { super.update(newWidget); widget.updateRenderObject(this, renderObject); _dirty = false; } }

RenderObjectElement 的 performRebuild方法

abstract class RenderObjectElement extends Element { @override void performRebuild() { widget.updateRenderObject(this, renderObject); _dirty = false; } }

widget.updateRenderObject每一种RenderObjectElement都会有自己的updateRenderObject处理方式,处理完成后如果需要重新计算大小宽高就会加到 PipelineOwner 的 _nodesNeedingLayout列表中,如果需要重新绘制就加到 PipelineOwner 的 _nodesNeedingPaint 列表中

剩下执行super.drawFrame() 这行代码就是调用 RendererBinding 类的 drawFrame 函数

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { @protected void drawFrame() { assert(renderView != null); // 调用 RenderView.performLayout(),遍历子节点,子节点在widget.updateRenderObject已经加入到列表内 // 调用每个节点的 layout(),RenderObject的排版数据,使得每个RenderObject最终都能有正确的大小和位置 pipelineOwner.flushLayout(); // 更新渲染对象,此阶段每个渲染对象都会了解其子项是否需要合成 // 在绘制阶段使用此信息选择如何实现裁剪等视觉效果 pipelineOwner.flushCompositingBits(); // 会调用 RenderView.paint() 最终触发各个节点的 paint(),最终生成一棵Layer Tree,并把绘制指令保存在Layer中 pipelineOwner.flushPaint(); if (sendFramesToEngine) { // 把Layer Tree提交给GPU renderView.compositeFrame(); // pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. _firstFrameSent = true; } } } setState 总结

总结一下 setState 过程

首先调用 markNeedsBuild 方法,将 element 的 dirty 标记为 true,表示需要重建 接着调用 scheduleBuildFor ,将当前的 element 添加到 _dirtyElements 列表中 调用buildOwner.buildScope,函数内部对 _dirtyElements 列表中的 element 调用 rebuild 函数 rebuild 函数调用 updateChild 循环更新子 element RenderObjectElement 调用 updateRenderObject ,对 RenderObject 更新 最后调用 pipelineOwner相关方法最后更新界面

更新流程图如下:

image.png



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有